//
//  Logger.swift
//  LogTest
//
//  Created by ellzu on 2021/6/30.
//

import Foundation

public extension Logger.Flag {
    static let Error: Logger.Flag = Logger.Flag(0x80_00_00_00)
    static let Debug: Logger.Flag = Logger.Flag(0x40_00_00_00)
    static let Info: Logger.Flag = Logger.Flag(0x20_00_00_00)
}

extension Logger.Module {
    static let Global: Logger.Module = Logger.Module("Global")
}

@inline(never) public func print(_ module: Logger.Module, _ flag: Logger.Flag, _ items: Any..., file: String = #file, line: Int = #line, separator: String = " ", terminator: String = "\n") -> Void {
    Logger.shared.print(module, flag, items, file: file, line: line, separator: separator, terminator: terminator)
}

public class Logger {
    public static let shared = Logger()
    public var defaultEnableFlag: Flag = .Error | .Debug | .Info
    public var enableFlags: [Module : Flag] = [:]
    public var logHandler: ((_ module: String, _ log: String, _ file: String, _ line: Int)->Void)? = nil
    
    public func print(_ module: Module, _ flag: Flag, _ items: [Any], file: String, line: Int, separator: String, terminator: String) {
        let enableFlag: Flag
        if let mFlag = enableFlags[module] {
            enableFlag = mFlag
        } else {
            enableFlag = defaultEnableFlag
        }
        guard enableFlag & flag != 0 else {
            return
        }
        let fileName: String
        if let fileNameIndex = file.lastIndex(of: Character("/")) {
            fileName = String(file.suffix(from: file.index(fileNameIndex, offsetBy: 1)))
        } else {
            fileName = file
        }
        
        var buffer: String = String()
        for i in 0..<items.count {
            if i == items.count - 1 {
                Swift.print(items[i], terminator: terminator, to: &buffer)
            } else {
                Swift.print(items[i], terminator: separator, to: &buffer)
            }
        }
        self.logHandler?(module.rawValue, buffer, fileName, line)
        
    }

}

//MARK: Module
public extension Logger {
    struct Module: Hashable{
        let rawValue: String
        public init(_ rawValue: String) {
            self.rawValue = rawValue
        }
        public static func == (left: Module, right: Module) -> Bool {
            return left.rawValue == right.rawValue
        }
    }
}


//MARK: Flag
public extension Logger {
    struct Flag {
        let rawValue: UInt32
        public init(_ rawValue: UInt32) {
            self.rawValue = rawValue
        }
        
        public static func - (left: Flag, right: Flag) -> Flag {
            return Flag(left.rawValue - right.rawValue)
        }
        public static func -= (left: inout Flag, right: Flag) -> Flag {
            left = left - right
            return left
        }
        public static func + (left: Flag, right: Flag) -> Flag {
            return Flag(left.rawValue + right.rawValue)
        }
        public static func += (left: inout Flag, right: Flag) -> Flag {
            left = left + right
            return left
        }
        public static func | (left: Flag, right: Flag) -> Flag {
            return Flag(left.rawValue | right.rawValue)
        }
        public static func |= (left: inout Flag, right: Flag) -> Flag {
            left = left | right
            return left
        }
        public static func & (left: Flag, right: Flag) -> Flag {
            return  Flag(left.rawValue & right.rawValue)
        }
        public static func &= (left: inout Flag, right: Flag) -> Flag {
            left = left & right
            return left
        }
        public static func == (left: Flag, right: Flag) -> Bool {
            return left.rawValue == right.rawValue
        }
        public static func == (left: Flag, right: UInt32) -> Bool {
            return left.rawValue == right
        }
        public static func == (left: UInt32, right: Flag) -> Bool {
            return left == right.rawValue
        }
        public static func != (left: Flag, right: Flag) -> Bool {
            return left.rawValue != right.rawValue
        }
        public static func != (left: Flag, right: UInt32) -> Bool {
            return left.rawValue != right
        }
        public static func != (left: UInt32, right: Flag) -> Bool {
            return left != right.rawValue
        }
    }
}
